/*
 * Copyright (C) 2020, All rights Reserved, Designed By
 * @author: maoyz
 * @Copyright: 2020-06-23 10:43  Inc. All rights reserved.
 */
package com.myz.design.aop.proxy;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.myz.design.aop.aspect.Aspect;
import com.myz.design.aop.interceptor.CglibInterceptor;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Constructor;

/**
 * 使用 cglib的 net.sf.cglib.proxy.Enhancer
 *
 * @author maoyz
 */
public class CglibProxyFactory extends ProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T proxy(T target, Aspect aspect) {
        final Class<?> targetClass = target.getClass();

        // Enhancer是CGLIB的字节码增强器，可以很方便的对类进行拓展
        net.sf.cglib.proxy.Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(targetClass);
        // 设置拦截类
        enhancer.setCallback(new CglibInterceptor(target, aspect));
        return create(enhancer, targetClass);
    }

    /**
     * 创建代理对象<br>
     * https://gitee.com/dromara/hutool/issues/I74EX7<br>
     * 某些对象存在非空参数构造，则需遍历查找需要的构造完成代理对象构建。
     *
     * @param <T>         代理对象类型
     * @param enhancer
     * @param targetClass 目标类型
     * @return 代理对象
     */
    @SuppressWarnings("unchecked")
    private static <T> T create(final net.sf.cglib.proxy.Enhancer enhancer, final Class<?> targetClass) {
        final Constructor<?>[] constructors = ReflectUtil.getConstructors(targetClass);
        Class<?>[] parameterTypes;
        Object[] values;
        IllegalArgumentException finalException = null;
        for (final Constructor<?> constructor : constructors) {
            parameterTypes = constructor.getParameterTypes();
            values = ClassUtil.getDefaultValues(parameterTypes);

            try {
                return (T) enhancer.create(parameterTypes, values);
            } catch (final IllegalArgumentException e) {
                //ignore
                finalException = e;
            }
        }
        if (null != finalException) {
            throw finalException;
        }

        throw new IllegalArgumentException("No constructor provided");
    }
}